package com.five.zhh.fiveinfive.room.connection;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.five.zhh.fiveinfive.exception.OperateException;
import com.five.zhh.fiveinfive.room.GameRoom;
import com.five.zhh.fiveinfive.room.GameRoomManager;
import com.five.zhh.fiveinfive.room.message.OperateTittle;
import com.five.zhh.fiveinfive.room.message.UserOperate;
import com.five.zhh.fiveinfive.room.message.UserOperateResult;
import com.five.zhh.fiveinfive.room.message.operate.*;
import com.five.zhh.fiveinfive.room.message.result.OperateExceptionResult;
import com.five.zhh.fiveinfive.room.message.result.PlayChessResult;
import com.five.zhh.fiveinfive.room.user.UserInfo;
import com.five.zhh.fiveinfive.room.user.UserInfoInRoom;
import jakarta.websocket.*;
import jakarta.websocket.server.ServerEndpoint;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;


@ServerEndpoint("/joinRoom")
@Component
@Scope("prototype")
@Data
@Slf4j
public class UserRoomConnection {

    private UserInfo userInfo;

    private GameRoom currentRoom;

    private Session session;


    @OnOpen
    public void onOpen(Session session) {
        Map<String, List<String>> parameter = session.getRequestParameterMap();
        String userName = parameter.get("userName").get(0);
        String roomCode = parameter.get("roomCode").get(0);
        // 如果有userId，则代表是重连，沿用之前的userId
        if (parameter.containsKey("userId")) {
            List<String> userId = parameter.get("userId");
            this.userInfo = new UserInfo(userId.get(0), userName);
        } else {
            this.userInfo = new UserInfo(StrUtil.isBlank(userName) ? "游客".concat(session.getRequestURI().getHost()) : userName);
        }
        this.session = session;
        this.currentRoom = GameRoomManager.getGameRoom(roomCode);
        boolean b = GameRoomManager.joinRoom(roomCode, this);
        if (!b) {
            throw new OperateException("加入房间失败");
        }
        UserInfoInRoom u = new UserInfoInRoom(this.userInfo, this.currentRoom.getUserRoleInRoom(this.userInfo));
        this.currentRoom.operate(
                new SystemBroadcastOperate(
                        currentRoom.getRoomCode(),
                        String.format("[%s]加入房间", this.userInfo.getUserName())
                )
        );
        SystemOperate so = new SystemOperate(u, SystemOperate.JOIN_ROOM_SUCCESS, "房间加入成功", roomCode);
        this.currentRoom.operate(so);
        so = new SystemOperate(u, SystemOperate.OTHER_JOIN, "其他玩家加入", roomCode);
        so.setSender(u);
        this.currentRoom.operate(so);
    }

    private UserOperate beforeOperate(String message) {
        UserOperate bean = JSONUtil.toBean(message, UserOperate.class);
        OperateTittle tittle = bean.getTittle();
        if (tittle.equals(OperateTittle.MESSAGE)) {
            // 玩家发送普通消息
            UserMessageOperate operate = JSONUtil.toBean(message, UserMessageOperate.class);
            operate.setRoomCode(this.currentRoom.getRoomCode());
            bean = operate;
        } else if (tittle.equals(OperateTittle.PLAY_CHESS)) {
            // 玩家进行下棋
            PlayChessOperate operate = JSONUtil.toBean(message, PlayChessOperate.class);
            operate.setRoomCode(this.currentRoom.getRoomCode());
            bean = operate;
        } else if (tittle.equals(OperateTittle.GIVE_UP)) {
            // 玩家进行投降或是否接受投降
            GiveUpOperate operate = JSONUtil.toBean(message, GiveUpOperate.class);
            operate.setRoomCode(this.currentRoom.getRoomCode());
            bean = operate;
        } else if (tittle.equals(OperateTittle.SYSTEM)) {
            SystemOperate operate = JSONUtil.toBean(message, SystemOperate.class);
            operate.setRoomCode(this.currentRoom.getRoomCode());
            bean = operate;
        }
        bean.setSender(new UserInfoInRoom(this.userInfo, this.currentRoom.getUserRoleInRoom(this.userInfo)));
        return bean;
    }

    @OnMessage
    public void onMessage(String message, Session session) {
        UserOperate bean = this.beforeOperate(message);
        UserOperateResult result = currentRoom.operate(bean);
        this.afterOperate(result);
    }

    public void afterOperate(UserOperateResult result) {
        if (result instanceof OperateExceptionResult oer) {
            this.currentRoom.operate(new SystemOperate(result.getReceiver(), SystemOperate.PLAY_CHESS_FAIL, oer.getMessage(), oer.getRoomCode()));
        } else if (result instanceof PlayChessResult pr) {
            if (!pr.isSuccess()) {
                this.currentRoom.operate(new SystemOperate(result.getReceiver(), SystemOperate.PLAY_CHESS_FAIL, pr.getMessage(), pr.getRoomCode()));
                return;
            }
            if (currentRoom.isWin(pr.getX(), pr.getY())) {
                WinnerOperate winnerOperate = new WinnerOperate(pr.getReceiver());
                winnerOperate.setRoomCode(this.currentRoom.getRoomCode());
                currentRoom.operate(winnerOperate);
            }
        }
    }

    @OnError
    public void onError(Session session, Throwable throwable) {
        try {
//            log.error("onError: {}", throwable.getMessage());
            throwable.printStackTrace();
            if (throwable instanceof OperateException e) {
                UserOperate userOperate = new ExceptionOperate(e.getMessage());
                session.getBasicRemote().sendText(JSONUtil.toJsonStr(userOperate));
                return;
            }
            /*if (currentRoom != null) {
                currentRoom.removeUser(this);
            }*/
        } catch (Exception e) {
            log.error("onError: {}, error: {}", e.getMessage(), throwable.getMessage());
        }
    }

    @OnClose
    public void onClose(Session session) {
        try {
            if (currentRoom != null) {
                currentRoom.removeUser(this);
            }
            if (null != session && session.isOpen()) {
                session.close();
            }
        } catch (Exception e) {
            log.error("onClose: {}", e.getMessage());
        }
    }

}
